﻿using System;
using System.Data;
using System.Data.Linq;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Threading;

using PickGold.Callers;
using PickGold.Handler;

namespace PickGold.Arguments
{
	/// <summary>
	/// 用来向 Command 对象表示一个参数，以及向该对象的 System.Data.DataSet 列映射表示参数（可选）。
	/// </summary>
	[Serializable]
	[ComVisible(true)]
	[ClassInterface(ClassInterfaceType.AutoDispatch)]
	public class DataParameterEventArgs : EventArgs, IDbDataParameter, IDataParameter
	{
		#region ctor

		/// <summary>
		/// ctor
		/// </summary>
		public DataParameterEventArgs()
		{
			this.DbType = DbType.AnsiString;
			this.Direction = ParameterDirection.Output;
			this.SourceVersion = DataRowVersion.Default;
			this.Size = -1;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="value"></param>
		public DataParameterEventArgs(object value)
		{
			this.DbType = GetType(value);
			this.Direction = ParameterDirection.InputOutput;
			this.SourceVersion = DataRowVersion.Default;
			if (value is DbType)
				value = null;
			this.Value = value;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="value"></param>
		/// <param name="size"></param>
		public DataParameterEventArgs(object value, int size)
		{
			this.DbType = GetType(value);
			this.Direction = ParameterDirection.InputOutput;
			this.SourceVersion = DataRowVersion.Default;
			if (value is DbType)
				value = null;
			this.Value = value;
			this.Size = size;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="value"></param>
		/// <param name="precision"></param>
		/// <param name="scale"></param>
		public DataParameterEventArgs(object value, byte precision, byte scale)
		{
			this.DbType = GetType(value);
			this.Direction = ParameterDirection.InputOutput;
			this.SourceVersion = DataRowVersion.Default;
			if (value is DbType)
				value = null;
			this.Value = value;
			this.Precision = precision;
			this.Scale = scale;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="value"></param>
		/// <param name="direction"></param>
		public DataParameterEventArgs(object value, ParameterDirection direction)
		{
			this.DbType = GetType(value);
			this.Direction = ParameterDirection.InputOutput;
			this.SourceVersion = DataRowVersion.Default;
			if (value is DbType)
				value = null;
			this.Value = value;
			this.Direction = direction;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="value"></param>
		/// <param name="direction"></param>
		/// <param name="size"></param>
		public DataParameterEventArgs(object value, ParameterDirection direction, int size)
		{
			this.DbType = GetType(value);
			this.Direction = ParameterDirection.InputOutput;
			this.SourceVersion = DataRowVersion.Default;
			if (value is DbType)
				value = null;
			this.Value = value;
			this.Direction = direction;
			this.Size = size;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="value"></param>
		/// <param name="direction"></param>
		/// <param name="precision"></param>
		/// <param name="scale"></param>
		public DataParameterEventArgs(object value, ParameterDirection direction, byte precision, byte scale)
		{
			this.DbType = GetType(value);
			this.Direction = direction;
			this.SourceVersion = DataRowVersion.Default;
			if (value is DbType)
				value = null;
			this.Value = value;
			this.Precision = precision;
			this.Scale = scale;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="type"></param>
		public DataParameterEventArgs(DbType type)
		{
			this.DbType = type;
			this.Direction = ParameterDirection.Output;
			this.SourceVersion = DataRowVersion.Default;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="type"></param>
		/// <param name="size"></param>
		public DataParameterEventArgs(DbType type, int size)
		{
			this.DbType = type;
			this.Direction = ParameterDirection.Output;
			this.SourceVersion = DataRowVersion.Default;
			this.Size = size;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="type"></param>
		/// <param name="precision"></param>
		/// <param name="scale"></param>
		public DataParameterEventArgs(DbType type, byte precision, byte scale)
		{
			this.DbType = type;
			this.Direction = ParameterDirection.Output;
			this.SourceVersion = DataRowVersion.Default;
			this.Precision = precision;
			this.Scale = scale;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="type"></param>
		public DataParameterEventArgs(Type type)
		{
			this.DbType = GetType(type);
			this.Direction = ParameterDirection.Output;
			this.SourceVersion = DataRowVersion.Default;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="type"></param>
		/// <param name="size"></param>
		public DataParameterEventArgs(Type type, int size)
		{
			this.DbType = GetType(type);
			this.Direction = ParameterDirection.Output;
			this.SourceVersion = DataRowVersion.Default;
			this.Size = size;
		}
		/// <summary>
		/// ctor
		/// </summary>
		/// <param name="type"></param>
		/// <param name="precision"></param>
		/// <param name="scale"></param>
		public DataParameterEventArgs(Type type, byte precision, byte scale)
		{
			this.DbType = GetType(type);
			this.Direction = ParameterDirection.Output;
			this.SourceVersion = DataRowVersion.Default;
			this.Precision = precision;
			this.Scale = scale;
		}

		#endregion

		/// <summary>
		/// 用于表示数据提供程序 Parameter 对象的 Value 属性的数字的最大位数。默认值为 0，它表示由数据提供程序设置 Value 的精度。
		/// </summary>
		public byte Precision;
		/// <summary>
		/// 要将 System.Data.OleDb.OleDbParameter.Value 解析为的小数位数。默认值为 0。
		/// </summary>
		public byte Scale;
		/// <summary>
		/// 列中数据的最大大小（以字节为单位）。从参数值推断默认值。
		/// </summary>
		public int Size;
		/// <summary>
		/// 获取或设置参数的 System.Data.DbType。System.Data.DbType 值之一。默认为 System.Data.DbType.String。
		/// </summary>
		public DbType DbType;
		/// <summary>
		/// 获取或设置一个值，该值指示参数是只可输入、只可输出、双向还是存储过程返回值参数。
		/// </summary>
		public ParameterDirection Direction;
		/// <summary>
		/// 获取一个值，该值指示参数是否接受 null 值。
		/// </summary>
		public bool IsNullable;
		/// <summary>
		/// 获取或设置 System.Data.IDataParameter 的名称。
		/// </summary>
		public string ParameterName;
		/// <summary>
		/// 获取或设置源列的名称，该源列映射到 System.Data.DataSet 并用于加载或返回 System.Data.IDataParameter.Value。
		/// </summary>
		public string SourceColumn;
		/// <summary>
		/// 获取或设置在加载 System.Data.IDataParameter.Value 时使用的 System.Data.DataRowVersion。
		/// </summary>
		public DataRowVersion SourceVersion;
		/// <summary>
		/// 获取或设置该参数的值。
		/// </summary>
		public object Value;

		#region IDbDataParameter 成员

		byte IDbDataParameter.Precision
		{
			get
			{
				return this.Precision;
			}
			set
			{
				this.Precision = value;
			}
		}

		byte IDbDataParameter.Scale
		{
			get
			{
				return this.Scale;
			}
			set
			{
				this.Scale = value;
			}
		}

		int IDbDataParameter.Size
		{
			get
			{
				return this.Size;
			}
			set
			{
				this.Size = value;
			}
		}

		DbType IDataParameter.DbType
		{
			get
			{
				return this.DbType;
			}
			set
			{
				this.DbType = value;
			}
		}

		ParameterDirection IDataParameter.Direction
		{
			get
			{
				return this.Direction;
			}
			set
			{
				this.Direction = value;
			}
		}

		bool IDataParameter.IsNullable
		{
			get
			{
				return this.IsNullable;
			}
		}

		string IDataParameter.ParameterName
		{
			get
			{
				return this.ParameterName;
			}
			set
			{
				this.ParameterName = value;
			}
		}

		string IDataParameter.SourceColumn
		{
			get
			{
				return this.SourceColumn;
			}
			set
			{
				this.SourceColumn = value;
			}
		}

		DataRowVersion IDataParameter.SourceVersion
		{
			get
			{
				return this.SourceVersion;
			}
			set
			{
				this.SourceVersion = value;
			}
		}

		object IDataParameter.Value
		{
			get
			{
				return this.Value;
			}
			set
			{
				this.Value = value;
			}
		}

		#endregion

		/// <summary/>
		public T CopyTo<T>(T target) where T : IDbDataParameter
		{
			target.DbType = this.DbType;
			target.Direction = this.Direction;
			target.ParameterName = this.ParameterName;
			target.Precision = this.Precision;
			target.Scale = this.Scale;
			target.Size = this.Size;
			target.SourceColumn = this.SourceColumn;
			target.SourceVersion = this.SourceVersion;
			target.Value = this.Value;
			return target;
		}
		/// <summary/>
		public static T CopyTo<T>(T target, IDbDataParameter source) where T : IDbDataParameter
		{
			var @this = source as DataParameterEventArgs;
			if (@this != null)
			{
				target.DbType = @this.DbType;
				target.Direction = @this.Direction;
				target.ParameterName = @this.ParameterName;
				target.Precision = @this.Precision;
				target.Scale = @this.Scale;
				target.Size = @this.Size;
				target.SourceColumn = @this.SourceColumn;
				target.SourceVersion = @this.SourceVersion;
				target.Value = @this.Value;
			}
			else
			{
				target.DbType = source.DbType;
				target.Direction = source.Direction;
				target.ParameterName = source.ParameterName;
				target.Precision = source.Precision;
				target.Scale = source.Scale;
				target.Size = source.Size;
				target.SourceColumn = source.SourceColumn;
				target.SourceVersion = source.SourceVersion;
				target.Value = source.Value;
			}
			return target;
		}
		/// <summary/>
		public static DbType GetType(object value, DbType defaultValue = default(DbType))
		{
			if (!(value is string) && !(value is byte[]))
			{
				var array = value as Array;
				if (array != null)
				{
					value = array.GetValue(0);
				}
				else
				{
					var ls = value as IList;
					if (ls != null)
						value = ls[0];
				}
			}
			if (value == null)
				return DbType.Xml;

			if (value is string || value is StringBuilder)
				return DbType.String;

			if (value is byte[] || value is Binary)
				return DbType.Binary;

			if (value is Guid)
				return DbType.Guid;

			if (value is DateTime)
				return DbType.DateTime2;

			if (value is decimal)
				return DbType.Currency;// DbType.Decimal;//

			if (value is double)
				return DbType.Double;

			if (value is float)
				return DbType.Single;

			if (value is long)
				return DbType.Int64;

			if (value is int)
				return DbType.Int32;

			if (value is short)
				return DbType.Int16;

			if (value is byte)
				return DbType.Byte;

			if (value is bool)
				return DbType.Boolean;

			if (value is DbType)
				return (DbType)value;

			return defaultValue;// default(DbType);
		}
		/// <summary/>
		public static DbType GetType(Type type, DbType defaultValue = default(DbType))
		{
			if (type == null)
				return DbType.Xml;

			if (type == typeof(string) || type == typeof(StringBuilder))
				return DbType.String;

			if (type == typeof(byte[]) || type == typeof(Binary))
				return DbType.Binary;

			if (type == typeof(Guid))
				return DbType.Guid;

			if (type == typeof(DateTime))
				return DbType.DateTime2;

			if (type == typeof(decimal))
				return DbType.Currency;// DbType.Decimal;//

			if (type == typeof(double))
				return DbType.Double;

			if (type == typeof(float))
				return DbType.Single;

			if (type == typeof(long))
				return DbType.Int64;

			if (type == typeof(int))
				return DbType.Int32;

			if (type == typeof(short))
				return DbType.Int16;

			if (type == typeof(byte))
				return DbType.Byte;

			if (type == typeof(bool))
				return DbType.Boolean;

			return defaultValue;// default(DbType);
		}
		/// <summary/>
		public static IDbDataParameter SetType(IDbDataParameter parmeter, object value, DbType defaultValue = default(DbType))
		{
			if (value != null || parmeter.DbType == DbType.Object)
			{
				if (value is DbType)
					parmeter.DbType = (DbType)value;
				else
					parmeter.DbType = GetType(value, defaultValue);
			}
			if (parmeter.Precision != 0 || parmeter.Scale != 0 || parmeter.Size != 0)
				return parmeter;

			switch (parmeter.DbType)
			{
				case DbType.AnsiString:
				case DbType.AnsiStringFixedLength:
				case DbType.Binary:
				case DbType.String:
				case DbType.StringFixedLength:
					parmeter.Size = -1;
					break;
				case DbType.Decimal:
				case DbType.Currency:
					parmeter.Precision = BitUtil.B08_02 + BitUtil.B08_04;
					parmeter.Scale = BitUtil.B08_02;
					break;
			}
			return parmeter;
		}
		/// <summary/>
		public static IDbDataParameter SetType(IDbDataParameter parmeter, Type type, DbType defaultValue = default(DbType))
		{
			if (type != null || parmeter.DbType == DbType.Object)
				parmeter.DbType = GetType(type, defaultValue);
			if (parmeter.Precision != 0 || parmeter.Scale != 0 || parmeter.Size != 0)
				return parmeter;

			switch (parmeter.DbType)
			{
				case DbType.AnsiString:
				case DbType.AnsiStringFixedLength:
				case DbType.Binary:
				case DbType.String:
				case DbType.StringFixedLength:
					parmeter.Size = -1;
					break;
				case DbType.Decimal:
				case DbType.Currency:
					parmeter.Precision = BitUtil.B08_02 + BitUtil.B08_04;
					parmeter.Scale = BitUtil.B08_02;
					break;
			}
			return parmeter;
		}
	}
}
